#include "include/App.h"
#include "include/Scripting.h"

using namespace Ei;

#if OGRE_PLATFORM != OGRE_PLATFORM_WIN32
static void ensureDirExists(const Ogre::String& dir)
{
        struct stat st;
        if (0 == stat(dir.c_str(),&st)) {
                if (!(st.st_mode && S_IFDIR)) {
                        std::cerr << "Can't create directory " << dir << "\n";
                        std::cerr << "Other file is in the way\n";
                        exit(1);
                }
        } else if (errno == ENOENT) {
                // create
                if (0 != mkdir(dir.c_str(),0770)) {
                        std::cerr << "Can't create directory " << dir << "\n";
                        perror(NULL);
                        exit(1);
                }
        } else {
                std::cerr << "Can't get acces to directory " << dir << "\n";
                perror(NULL);
                exit(1);
        }
}
#endif

Scripting::Scripting(std::vector<const char*> script_names)
{
        mLua = lua_open();
        luabind::open(mLua);
        luaL_openlibs(mLua);

#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
        mCfgDir = ".";
#else
        mCfgDir = Ogre::String(getenv("HOME")) + SLASH + ".ei4";
        ensureDirExists(mCfgDir);
#endif

	loadDefaults();
	load(script_names);
}

Scripting::~Scripting()
{
        lua_close(mLua);
        mLua = NULL;
}

void Scripting::loadDefaults()
{
        // prepare global configuration tables and "factory settings"
        // ogre is ok with unix slashes on windows
        exec("mono = 0; stereo = 1; \
        win = { fullscreen = true, width = 1280, height = 1024, \
        renderSystem = 'OpenGL Rendering Subsystem', \
        displayMode = mono } \
        physics = {} \
	net = { soundhost = '127.0.0.1' } \
        dolly = { size = {0.3,1.8,0.5}, mass = 75.0 } \
	game = { n_snacks = 10 } \
        scene = 'resources/media/doolhof1/labyrinth01_ogre_exporter.OSM' \
        particles = {useParticles = true, default ='EIParticles/Fog'} \
        material  = {UserBody = 'Ei4/UserBody' }"
	);

        load(getDefaultsPath());
}

void Scripting::saveDefaults(const char * scene_path)
{
        std::ofstream os(getDefaultsPath().c_str());
        saveTable("win",os);
        saveTable("dolly",os);
        saveTable("net",os);
        saveTable("game",os);
        saveTable("particles",os);
        saveTable("material",os);
        os << "scene=" << escapeString(scene_path);
}

void Scripting::saveAudio(const char * scene_path)
{
        std::ofstream os(getDefaultsPath().c_str());
        saveTable("win",os);
        saveTable("dolly",os);
        saveTable("net",os);
        saveTable("game",os);
        saveTable("particles",os);
        saveTable("material",os);
        os << "scene=" << escapeString(scene_path);
}


void Scripting::saveTable(const char* table, std::ofstream& os)
{
        lua_getglobal(mLua, table);
        lua_pushnil(mLua);
        os << table << " = {\n";
        while(lua_next(mLua, -2)) {
                os << toString(-2) << " = "
                   << toString(-1, true) << ",\n";

                lua_pop(mLua, 1); // pop the value, keep the key
        }
        os << "}\n";
}

void Scripting::saveString(const char* var, std::ofstream& os)
{
        os << var << " = ";
        lua_getglobal(mLua, var);
        os << toString(-1) << "\n";
}

void Scripting::load(std::vector<const char*>& scripts)
{
        std::vector<const char*>::iterator sit = scripts.begin();
        for(;sit != scripts.end(); sit++) {
                load(*sit);
        }
}

void Scripting::load(const Ogre::String& fname)
{
        load(fname.c_str());
}

void Scripting::load(const char* fname)
{
        luaL_dofile(mLua, fname);
}

void Scripting::exec(const Ogre::String& script)
{
        luaL_dostring(mLua, script.c_str());
}

int Scripting::getTableInt(const char * table, const char * name)
{
        lua_getglobal(mLua, table);
        lua_pushstring(mLua, name);
        lua_gettable(mLua, -2);
        int ret = luaL_checkint(mLua, -1);
        lua_pop(mLua, 2);
        return ret;
}

float Scripting::getTableFloat(const char * table, const char * name)
{
        lua_getglobal(mLua, table);
        lua_pushstring(mLua, name);
        lua_gettable(mLua, -2);
        float ret = (float)luaL_checknumber(mLua, -1);
        lua_pop(mLua, 2);
        return ret;
}

double Scripting::getTableDouble(const char * table, const char * name)
{
        lua_getglobal(mLua, table);
        lua_pushstring(mLua, name);
        lua_gettable(mLua, -2);
        double ret = (double)luaL_checknumber(mLua, -1);
        lua_pop(mLua, 2);
        return ret;
}

bool Scripting::getTableBool(const char * table, const char * name)
{
        lua_getglobal(mLua, table);
        lua_pushstring(mLua, name);
        lua_gettable(mLua, -2);
        bool res = (bool)lua_toboolean(mLua, -1);
        lua_pop(mLua, 2);
        return res;
}

const char* Scripting::getTableCString(const char * table, const char * name)
{
        lua_getglobal(mLua, table);
        lua_pushstring(mLua, name);
        lua_gettable(mLua, -2);
        lua_getglobal(mLua, "tostring");
        lua_pushvalue(mLua, -2);
        lua_pcall(mLua, 1, 1, 0);
        const char * res = lua_tostring(mLua, -1);
        lua_pop(mLua, 3);
        return res;
}

const char* Scripting::getGlobalCString(const char * name)
{
        lua_getglobal(mLua, "tostring");
        lua_getglobal(mLua, name);
        lua_pcall(mLua, 1, 1, 0);
        const char * res = lua_tostring(mLua, -1);
        lua_pop(mLua, 1);
        return res;
}

Ogre::Vector3 Scripting::getTableVector3(const char * table,
                                           const char * name)
{
        Ogre::Vector3 result;

        // get the array onto the top of the stack
        lua_getglobal(mLua, table);
        lua_pushstring(mLua, name);
        lua_gettable(mLua, -2);

        // figure out the coords
        for(int i=0; i<3; i++) {
                lua_pushinteger(mLua, (lua_Integer)(i+1));
                lua_gettable(mLua, -2);
                result[i] = lua_tonumber(mLua, -1);
                lua_pop(mLua, 1);
        }

        lua_pop(mLua, 1);
        return result;
}

void Scripting::tableToPairs(const char * table,
                               Ogre::NameValuePairList* pairs)
{
        lua_getglobal(mLua, table);

        lua_pushnil(mLua);
        while(lua_next(mLua, -2)) {
                pairs->insert(Ogre::NameValuePairList::
                        value_type(toString(-2),toString(-1)));
                lua_pop(mLua, 1); // pop the value, keep the key
        }
}

Ogre::String Scripting::toString(int idx, bool quote)
{
        if (idx<0)
                idx = lua_gettop(mLua) + idx + 1;
        // table ?
        if (lua_istable(mLua, idx)) {
                std::stringstream os;
                os << "{\n";
                lua_pushnil(mLua);
                while(lua_next(mLua, -2)) {
                        os << "[" << toString(-2, true)
                           << "]=" << toString(-1, true) << ",\n";

                        lua_pop(mLua, 1); // pop the value, keep the key
                }
                os << "}\n";
                return os.str();

        } else if (quote &&
                   // in lua number isstring()!
                   !lua_isnumber(mLua, idx) &&
                   lua_isstring(mLua,idx)) {
                lua_getglobal(mLua, "string");
                lua_getfield(mLua, -1, "format");
                lua_pushstring(mLua, "%q"); // format string
                lua_pushvalue(mLua, idx);
                lua_pcall(mLua, 2, 1, 0);
                Ogre::String res(lua_tostring(mLua, -1));
                lua_pop(mLua, 2);
                return res;
        } else {
                lua_getglobal(mLua, "tostring");
                lua_pushvalue(mLua, idx);
                lua_pcall(mLua, 1, 1, 0);
                Ogre::String res(lua_tostring(mLua, -1));
                lua_pop(mLua, 1);
                return res;
        }
}

Ogre::String Scripting::escapeString(const Ogre::String& src)
{
        lua_getglobal(mLua, "string");
        lua_getfield(mLua, -1, "format");
        lua_pushstring(mLua, "%q"); // format string
        lua_pushstring(mLua, src.c_str());
        lua_pcall(mLua, 2, 1, 0);
        Ogre::String res(lua_tostring(mLua, -1));
        lua_pop(mLua, 2);
        return res;
}
